import tkinter as tk
from tkinter import messagebox, ttk
import time
from threading import Thread

class HanoiTowerGame:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("汉诺塔游戏 - 自动版")
        self.root.geometry("900x650")
        
        # 控制面板增强
        self.control_frame = tk.Frame(self.root)
        self.control_frame.pack(pady=10)
        
        # 层数设置
        tk.Label(self.control_frame, text="层数（1-10）:").pack(side=tk.LEFT)
        self.level_entry = tk.Entry(self.control_frame, width=4)
        self.level_entry.pack(side=tk.LEFT, padx=5)
        self.level_entry.insert(0, "3")
        
        # 速度控制
        tk.Label(self.control_frame, text="速度:").pack(side=tk.LEFT, padx=(10,0))
        self.speed_scale = ttk.Scale(self.control_frame, from_=1, to=10, 
                                   value=5, orient=tk.HORIZONTAL, length=100)
        self.speed_scale.pack(side=tk.LEFT)
        
        # 操作按钮
        self.btn_frame = tk.Frame(self.control_frame)
        self.btn_frame.pack(side=tk.LEFT, padx=10)
        self.start_btn = tk.Button(self.btn_frame, text="自动解决", command=self.auto_solve)
        self.start_btn.pack(side=tk.LEFT)
        self.reset_btn = tk.Button(self.btn_frame, text="重新开始", command=self.reset_game)
        self.reset_btn.pack(side=tk.LEFT, padx=5)
        tk.Button(self.btn_frame, text="退出", command=self.root.quit).pack(side=tk.LEFT)
        
        # 游戏画布
        self.canvas = tk.Canvas(self.root, width=860, height=450, bg='white')
        self.canvas.pack()
        
        # 游戏参数
        self.tower_height = 300
        self.disk_height = 28
        self.base_y = 400
        self.colors = ['#FF6666', '#66FF66', '#6666FF', '#FFCC66', '#CC66FF',
                      '#FF99CC', '#99CCFF', '#CCFF99', '#FFD700', '#7B68EE']
        self.pegs = {'A': 200, 'B': 430, 'C': 660}
        self.auto_running = False
        
        # 初始设置
        self.reset_game()
        self.setup_events()

    def setup_events(self):
        """事件绑定"""
        self.canvas.bind("<Button-1>", self.start_drag)
        self.canvas.bind("<B1-Motion>", self.during_drag)
        self.canvas.bind("<ButtonRelease-1>", self.end_drag)

    def reset_game(self):
        """重置游戏状态"""
        self.auto_running = False
        self.canvas.delete("all")
        self.disk_objects = {}
        
        try:
            self.num_disks = int(self.level_entry.get())
            if not 1 <= self.num_disks <= 10:
                raise ValueError
        except ValueError:
            messagebox.showerror("错误", "请输入1-10之间的整数")
            self.level_entry.delete(0, tk.END)
            self.level_entry.insert(0, "3")
            self.num_disks = 3

        self.disks = {
            'A': list(range(self.num_disks, 0, -1)),
            'B': [],
            'C': []
        }

        self.draw_base()
        self.create_disks()
        self.toggle_controls(True)

    def toggle_controls(self, enable):
        """切换控件状态"""
        state = tk.NORMAL if enable else tk.DISABLED
        self.start_btn.config(state=state)
        self.reset_btn.config(state=state)
        self.level_entry.config(state=state)

    def auto_solve(self):
        """启动自动求解"""
        if not self.auto_running:
            self.auto_running = True
            self.toggle_controls(False)
            Thread(target=self.solve_hanoi).start()

    def solve_hanoi(self):
        """汉诺塔求解算法"""
        def move_disk(n, source, target, auxiliary):
            if n == 1:
                self.animate_move(source, target)
                return
            move_disk(n-1, source, auxiliary, target)
            self.animate_move(source, target)
            move_disk(n-1, auxiliary, target, source)

        try:
            move_disk(self.num_disks, 'A', 'C', 'B')
        finally:
            self.auto_running = False
            self.toggle_controls(True)

    def animate_move(self, source, target):
        """执行动画移动"""
        if not self.auto_running:
            return
        
        # 获取要移动的圆盘
        disk_size = self.disks[source][-1]
        
        # 模拟点击操作
        disk_id = next(k for k, v in self.disk_objects.items() if v == (source, disk_size))
        self.source_peg = source
        self.current_disk = disk_id
        
        # 计算目标位置
        x = self.pegs[target]
        y = self.base_y - self.disk_height * (len(self.disks[target]) + 1)
        
        # 计算移动距离
        start_x = self.pegs[source]
        start_y = self.base_y - self.disk_height * len(self.disks[source])
        dx = x - start_x
        dy = y - start_y
        
        # 动画步骤
        steps = 20
        step_dx = dx / steps
        step_dy = dy / steps
        
        for _ in range(steps):
            if not self.auto_running:
                return
            self.canvas.move(self.current_disk, step_dx, step_dy)
            self.canvas.update()
            time.sleep(0.05 / (self.speed_scale.get() * 0.5))
        
        # 更新数据结构
        self.disks[source].pop()
        self.disks[target].append(disk_size)
        self.disk_objects[self.current_disk] = (target, disk_size)
        
        # 胜利检测
        if len(self.disks['C']) == self.num_disks:
            messagebox.showinfo("胜利", "自动求解完成！")
            self.reset_game()

    def draw_base(self):
        """绘制底座和柱子（加大尺寸）"""
        # 加宽底座
        self.canvas.create_rectangle(50, self.base_y, 850, self.base_y+10, 
                                   fill='#996633', outline='')
        # 加粗柱子
        for x in self.pegs.values():
            self.canvas.create_rectangle(x-15, self.base_y-self.tower_height, 
                                        x+15, self.base_y, 
                                        fill='#663300', outline='')

    def create_disks(self):
        """创建圆盘（加大尺寸）"""
        max_width = 180  # 加大最大宽度
        min_width = 60   # 加大最小宽度
        width_step = (max_width - min_width) / max(1, (self.num_disks - 1))

        for size in range(self.num_disks, 0, -1):
            peg = 'A'
            index = self.disks[peg].index(size)
            width = min_width + (size-1)*width_step

            x = self.pegs[peg]
            y = self.base_y - self.disk_height * (index + 1)

            disk = self.canvas.create_rectangle(
                x - width/2, y - self.disk_height,
                x + width/2, y,
                fill=self.colors[size % len(self.colors)],
                outline='black',
                tags='disk'
            )
            self.disk_objects[disk] = (peg, size)

    # 保持原有的拖动处理方法（根据新尺寸调整坐标计算）
    def start_drag(self, event):
        """开始拖动"""
        items = self.canvas.find_overlapping(event.x-5, event.y-5, event.x+5, event.y+5)
        valid_disks = [item for item in items if item in self.disk_objects]
        
        if not valid_disks:
            return
        
        disk = valid_disks[-1]
        peg, size = self.disk_objects[disk]
        
        if self.disks[peg][-1] != size:
            messagebox.showwarning("错误", "只能移动最顶部的圆盘！")
            return
        
        self.dragging = True
        self.current_disk = disk
        self.source_peg = peg
        
        coords = self.canvas.coords(disk)
        disk_center_x = (coords[0] + coords[2]) / 2
        disk_center_y = (coords[1] + coords[3]) / 2
        self.offset_x = event.x - disk_center_x
        self.offset_y = event.y - disk_center_y
        
        self.canvas.tag_raise(disk)

    def during_drag(self, event):
        """拖动过程中"""
        if not self.dragging:
            return
        
        new_x = event.x - self.offset_x
        new_y = event.y - self.offset_y
        
        self.canvas.coords(
            self.current_disk,
            new_x - (self.canvas.coords(self.current_disk)[2] - self.canvas.coords(self.current_disk)[0])/2,
            new_y - self.disk_height,
            new_x + (self.canvas.coords(self.current_disk)[2] - self.canvas.coords(self.current_disk)[0])/2,
            new_y
        )

    def end_drag(self, event):
        """结束拖动"""
        if not self.dragging:
            return
        
        self.dragging = False
        target_peg = self.get_nearest_peg(event.x)
        disk_size = self.disk_objects[self.current_disk][1]
        
        if not target_peg:
            self.reset_disk_position()
            return
        
        if target_peg == self.source_peg:
            self.reset_disk_position()
            return
        
        if self.disks[target_peg] and self.disks[target_peg][-1] < disk_size:
            messagebox.showwarning("错误", f"不能把大盘{disk_size}放在小盘{self.disks[target_peg][-1]}上！")
            self.reset_disk_position()
            return
        
        self.move_disk(target_peg)
        
        if len(self.disks['C']) == self.num_disks:
            messagebox.showinfo("胜利", "恭喜完成挑战！")
            self.reset_game()

    def get_nearest_peg(self, x):
        """获取最近的柱子"""
        distances = {abs(x - pos): peg for peg, pos in self.pegs.items()}
        min_dist = min(distances.keys())
        return distances[min_dist] if min_dist < 120 else None  # 加大捕捉范围

    def reset_disk_position(self):
        """重置位置"""
        peg = self.source_peg
        size = self.disk_objects[self.current_disk][1]
        index = self.disks[peg].index(size)
        
        x = self.pegs[peg]
        y = self.base_y - self.disk_height * (index + 1)
        
        coords = self.canvas.coords(self.current_disk)
        width = coords[2] - coords[0]
        
        self.canvas.coords(
            self.current_disk,
            x - width/2,
            y - self.disk_height,
            x + width/2,
            y
        )

    def move_disk(self, target_peg):
        """正式移动"""
        size = self.disk_objects[self.current_disk][1]
        self.disks[self.source_peg].remove(size)
        self.disks[target_peg].append(size)
        
        x = self.pegs[target_peg]
        y = self.base_y - self.disk_height * len(self.disks[target_peg])
        
        coords = self.canvas.coords(self.current_disk)
        width = coords[2] - coords[0]
        
        self.canvas.coords(
            self.current_disk,
            x - width/2,
            y - self.disk_height,
            x + width/2,
            y
        )
        self.disk_objects[self.current_disk] = (target_peg, size)

    def run(self):
        self.root.mainloop()

if __name__ == "__main__":
    game = HanoiTowerGame()
    game.run()